package com.ape.mapsa.common;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.persistence.PostLoad;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;

import org.apache.log4j.Logger;
import org.primefaces.context.RequestContext;
import org.primefaces.event.CloseEvent;
import org.primefaces.event.TransferEvent;
import org.primefaces.model.DualListModel;
import org.springframework.dao.DataIntegrityViolationException;

import com.ape.mapsa.model.Role;
import com.ape.mapsa.model.User;
import com.ape.mapsa.model.UserRole;
import com.ape.mapsa.service.IUserService;

@ManagedBean(name = "userManagerMB")
@ViewScoped
public class UserManagerBean implements Serializable {

	private static final long serialVersionUID = -4433130200892833435L;

	private static final Logger log = Logger.getLogger(UserManagerBean.class);

	private User selectedUser;
	private User lastUser;
	private List<User> userList;
	private DualListModel<Role> roles;
	private List<Role> allRoles;
	private boolean rowSelected;
	private boolean activeUser;

	private User newUser;

	// Spring User Service is injected...
	@ManagedProperty(value = "#{UserService}")
	IUserService userService;

	@ManagedProperty(value = "#{userMB}")
	private UserBean userBean;

	@PostConstruct
	public void initialize() {
		log.info("Initializing UserManagerBean");
		userList = new ArrayList<User>();
		userList.addAll(getUserService().getUsers());
		allRoles = getUserService().getAllRoles();
		List<Role> sourceRoles = new ArrayList<Role>();
		sourceRoles.addAll(allRoles);
		setRoles(new DualListModel<Role>(sourceRoles, new ArrayList<Role>()));
	}

	public void rowSelected() {
		log.debug("rowSelected");
		RequestContext requestContext = RequestContext.getCurrentInstance();
		if (newUser != null) {
			FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Los cambios no fueron guardados", selectedUser.getEmail());
			FacesContext.getCurrentInstance().addMessage(null, msg);
			requestContext.addCallbackParam("isValid", false);
			selectedUser = lastUser;
			return;
		} else {
			if (userChanged()) {
				// Es nuevo y no fue guardado
				FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Se cancelo la creacion de un usuario nuevo", selectedUser.getEmail());
				FacesContext.getCurrentInstance().addMessage(null, msg);
				requestContext.addCallbackParam("isValid", false);
				selectedUser = lastUser;
				return;
			}
		}

		requestContext.addCallbackParam("isValid", true);
		List<UserRole> currentUserRoles = selectedUser.getRoles();
		List<Role> currentRoles = new ArrayList<Role>();
		if (currentUserRoles != null) {
			for (UserRole role : currentUserRoles) {
				currentRoles.add(role.getRole());
			}
		}

		roles.setTarget(currentRoles);
		List<Role> sourceRoles = new ArrayList<Role>();
		for (Role userRole : allRoles) {
			if (!currentRoles.contains(userRole)) {
				sourceRoles.add(userRole);
			}
		}
		roles.setSource(sourceRoles);

		rowSelected = selectedUser != null;
		activeUser = selectedUser.getActive() == 1;
		lastUser = selectedUser;
	}

	public void cancelarEdicion(ActionEvent actionEvent) {
		log.debug("cancelarEdicion");
		if (selectedUser != null) {
			if (newUser == null) {
				userList = userService.getUsers();
				rowSelected = true;
				activeUser = selectedUser.getActive() == 1;
			} else {
				for (User user : userList) {
					if (user.getId() == selectedUser.getId()) {
						userList.remove(user);
						selectedUser = null;
						rowSelected = false;
						newUser = null;
						break;
					}
				}
			}
		}
	}

	public void saveSelectedUser(ActionEvent actionEvent) {
		log.debug("saveSelectedUser");
		List<Role> targetRoles = roles.getTarget();
		List<UserRole> newUserRoles = new ArrayList<UserRole>();
		for (Role role : targetRoles) {
			for (Role dbrole : allRoles) {
				if (role.getRoleName().equals(dbrole.getRoleName())) {
					role.setRoleName(dbrole.getRoleName());
					break;
				}
			}
			UserRole newRole = new UserRole();
			newRole.setRole(role);
			newRole.setUser(selectedUser);
			newUserRoles.add(newRole);
		}
	
		User oldUser = userService.getById(selectedUser.getId());
		if (newUser == null) {
			userService.removeUserRoles(oldUser);
			userService.updateUser(selectedUser);
		}
		selectedUser.setRoles(newUserRoles);

		try {
			FacesMessage msg = null;
			if (newUser == null) {
				selectedUser.setLastUpdatedBy(userBean.getEmail());
				selectedUser.setLastUpdatedDate(new Date());
				userService.updateUser(selectedUser);
				msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Usuario modificado", selectedUser.getEmail());
			} else {
				selectedUser.setCreatedBy(userBean.getEmail());
				userService.addUser(selectedUser);
				msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Usuario agregado", selectedUser.getEmail());
				newUser = null;
			}
			if (msg != null) {
				FacesContext.getCurrentInstance().addMessage(null, msg);
			}
		} catch (DataIntegrityViolationException duplicated) {
			RequestContext requestContext = RequestContext.getCurrentInstance();
			FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "Email duplicado.");
			requestContext.addCallbackParam("validationFailed", true);
			FacesContext.getCurrentInstance().addMessage("editForm:notemail", msg);
		} catch (Exception ex) {
			log.error(ex);
			ex.printStackTrace();
			FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "Los datos no fueron guardados");
			FacesContext.getCurrentInstance().addMessage(null, msg);
		}
	}

	public void onTransfer(TransferEvent event) {
		StringBuilder builder = new StringBuilder();
		for (Object item : event.getItems()) {
			builder.append(((Role) item).getRoleName()).append("<br />");
		}

		FacesMessage msg = new FacesMessage();
		msg.setSeverity(FacesMessage.SEVERITY_INFO);
		msg.setSummary("Items Transferred");
		msg.setDetail(builder.toString());

		FacesContext.getCurrentInstance().addMessage(null, msg);
	}

	public void newUser(ActionEvent actionEvent) {
		log.debug("newUser");
		User newUser = new User();
		Long newId = 2L;
		for (User user : userList) {
			if (user.getId() >= newId) {
				newId = user.getId() + 1L;
				log.info("new id: " + newId);
				newUser.setId(newId);
			}
		}
		newUser.setId(newId);
		selectedUser = null;

		List<Role> sourceRoles = new ArrayList<Role>();
		sourceRoles.addAll(allRoles);
		setRoles(new DualListModel<Role>(sourceRoles, new ArrayList<Role>()));

		userList.add(newUser);
		selectedUser = newUser;
		lastUser = newUser;
		this.newUser = newUser;
	}

	public void closeUserEdition(ActionEvent actionEvent) {
		log.debug("closeUserEdition");
		RequestContext requestContext = RequestContext.getCurrentInstance();
		if (selectedUser != null) {
			if (newUser == null) {
				if (userChanged()) {
					FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Los cambios no fueron guardados", selectedUser.getEmail());
					FacesContext.getCurrentInstance().addMessage(null, msg);
					requestContext.addCallbackParam("isValid", false);
					return;
				}
			} else {
				// Es nuevo y no fue guardado
				FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Se cancelo la creacion de un usuario nuevo", selectedUser.getEmail());
				FacesContext.getCurrentInstance().addMessage(null, msg);
				requestContext.addCallbackParam("isValid", false);
				return;
			}
		}
		requestContext.addCallbackParam("isValid", true);
	}

	private boolean userChanged() {
		for (User user : userList) {
			if (user.getId() == selectedUser.getId()) {
				if (!user.equals(userService.getById(user.getId()))) {
					return true;
				}
				break;
			}
		}
		return false;
	}

	public void handleClose(CloseEvent event) {
		log.debug("handleClose");
		if (selectedUser != null) {
			if (newUser != null) {
				for (User user : userList) {
					if (user.getId() == newUser.getId()) {
						userList.remove(user);
						newUser = null;
						break;
					}
				}
				// Es nuevo y no fue guardado
				FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Se cancelo la creacion de un usuario nuevo", selectedUser.getEmail());
				FacesContext.getCurrentInstance().addMessage(null, msg);
			}
		}
		selectedUser = null;
		rowSelected = false;
	}

	public void activarUsuario(ActionEvent actionEvent) {
		log.debug("activarUsuario");
		for (User user : userList) {
			if (user.equals(selectedUser)) {
				try {
					user.setLastUpdatedDate(new Date());
					user.setLastUpdatedBy(userBean.getEmail());
					user.setActive(1);
					userService.updateUser(user);
					FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Usuario activado", selectedUser.getEmail());
					FacesContext.getCurrentInstance().addMessage(null, msg);
				} catch (Exception ex) {
					log.error(ex);
					FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "No se pudo eliminar a " + selectedUser.getFirstName());
					FacesContext.getCurrentInstance().addMessage(null, msg);
				}
				break;
			}
		}
	}

	public void deleteSelectedUser(ActionEvent actionEvent) {
		log.debug("deleteSelectedUser");
		for (User user : userList) {
			if (user.equals(selectedUser)) {
				try {
					user.setLastUpdatedDate(new Date());
					user.setLastUpdatedBy(userBean.getEmail());
					user.setActive(0);
					userService.updateUser(user);
					FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Usuario desactivado", selectedUser.getEmail());
					FacesContext.getCurrentInstance().addMessage(null, msg);
				} catch (Exception ex) {
					log.error(ex);
					FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "No se pudo eliminar a " + selectedUser.getFirstName());
					FacesContext.getCurrentInstance().addMessage(null, msg);
				}
				break;
			}
		}
	}

	public User getSelectedUser() {
		return selectedUser;
	}

	public void setSelectedUser(User selectedUser) {
		this.selectedUser = selectedUser;
	}

	public List<User> getUserList() {
		return userList;
	}

	public void setUserList(List<User> userList) {
		this.userList = userList;
	}

	public IUserService getUserService() {
		return userService;
	}

	public void setUserService(IUserService userService) {
		this.userService = userService;
	}

	public UserBean getUserBean() {
		return userBean;
	}

	public void setUserBean(UserBean userBean) {
		this.userBean = userBean;
	}

	public DualListModel<Role> getRoles() {
		return roles;
	}

	public void setRoles(DualListModel<Role> roles) {
		this.roles = roles;
	}

	public boolean isRowSelected() {
		return rowSelected;
	}

	public void setRowSelected(boolean rowSelected) {
		this.rowSelected = rowSelected;
	}

	public boolean isActiveUser() {
		return activeUser;
	}

	public void setActiveUser(boolean activeUser) {
		this.activeUser = activeUser;
	}

	public User getNewUser() {
		return newUser;
	}

	public void setNewUser(User newUser) {
		this.newUser = newUser;
	}

	@PostLoad
	public void postload() {
		log.info("USER MANAGER BEAN LOADED");
	}

	@PreDestroy
	public void predestroy() {
		log.info("USER MANAGER BEAN IS GOING TO BE DESTROYED");
	}

	@PostRemove
	public void postremove() {
		log.info("USER MANAGER BEAN REMOVED");
	}

	@PostUpdate
	public void postupdate() {
		log.info("USER MANAGER post update");
	}
}
